/*
 * RentalDAO.java
 *
 * Created on March 22, 2007, 1:45 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package edu.byu.isys413.group1E.data;


import edu.byu.isys413.group1E.handlers.ConnectionPool;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import edu.byu.isys413.group1E.*;
import java.sql.*;
import java.util.ArrayList;
import edu.byu.isys413.group1E.data.*;
import edu.byu.isys413.group1E.handlers.*;

/**
 * @author Jon Tehero
 * Isys 413 - Intex 2 - Milestone 3
 * April 02, 2007
 */
public class RentalDAO extends RevenueSourceDAO{
    private static RentalDAO instance = null;
    
//******************************************************************************
//   SINGLETON PATTERN
//******************************************************************************
    /** Using singleton pattern to call an instance of the RentalDAO **/
    public RentalDAO(){}
    /**Retrieves an instance of the TransactionDAO object**/
    public static synchronized RentalDAO getInstance() {
        if (instance == null) {
            instance = new RentalDAO();
        }
        return instance;
    }
    
//******************************************************************************
//   CREATE
//******************************************************************************
    /**
     * Creates a new Rental Business Object with a newly generated GUID.
     *
     * @return a newly created RentalBO with unique GUID
     * @throws java.lang.Exception to catch any errors in the creation process.
     */
    public RevenueSourceBO create() throws Exception{
        String id = GUID.generate();
        RentalBO bo = new RentalBO(id);
        bo.setType("rental");
        Cache c = Cache.getInstance();
        c.put(bo.getRevSrcID(), bo);
        return bo;
    }
    /**
     * Creates a new Rental Business Object with a newly generated GUID.
     *
     * @return a newly created RentalBO with unique GUID
     * @throws java.lang.Exception to catch any errors in the creation process.
     */
    public RevenueSourceBO create(ForRentBO frbo) throws Exception{
        String id = GUID.generate();
        RentalBO bo = new RentalBO(id);
        bo.setProduct(frbo);
        
        //ForRentBO frbo = new (ForRentBO)ForRentDAO.getInstance().create();
        bo.setType("rental");
        Cache c = Cache.getInstance();
        c.put(bo.getRevSrcID(), bo);
        return bo;
    }
    
//******************************************************************************
//   READ - Incorporates 2 Read Methods (1 for external use, 1 for internal),
//          and 1 ReadAll Method
//******************************************************************************
    /**
     * Creates a new RentalBO object with a newly generated GUID.
     *
     * @return a newly created FeeBO with unique GUID
     * @throws java.lang.Exception to catch any errors in the creation process.
     */
    public RevenueSourceBO read(String id) throws SQLException, DataException, ConnectionPoolException {
        // check to see if id in the cache
        Cache c = Cache.getInstance();
        RentalBO rental = (RentalBO)c.get(id);
        // if so, return it immediately
        if (rental != null){
            return rental;
        }
        // Otherwise, create a new rental object with the correct key, save it to
        // the cache, and call super.read to fill in its data otherwise, get it
        // from the database.
        try {
            // retrieve a database connection from the pool
            Connection conn = ConnectionPool.getInstance().get();
            // call read with a connection
            rental = (RentalBO)this.read(id, conn);
            conn.commit();
            // release the connection
            ConnectionPool.getInstance().release(conn);
        }catch (SQLException e) {
            //rollback
            throw new DataException("Could not retrieve record for id = " + id, e);
        }catch (ConnectionPoolException x) {
            //rollback
            throw new ConnectionPoolException("Could not establish connection to database.", x);
        }
        //Releases the connection back to the pool
        return rental;
    }
    /**
     * This is the public read method.  It is what is called when
     * the user (controller) code wants to read a rental object
     * into the database.
     */
    public synchronized RevenueSourceBO read(String id, Connection conn) throws SQLException, DataException, ConnectionPoolException {
        // check to see if id in the cache
        Cache c = Cache.getInstance();
        RentalBO rental = (RentalBO)c.get(id);
        // If the Rental Object has something in it (I.E. It is not empty) it is returned immediately
        if (rental != null){
            return rental;
        }
        //put it to the cache so the super.read can get it
        rental = new RentalBO(id);
        c.put(id, rental);
        // if not in the cache, get a result set from
        // a SELECT * FROM table WHERE id=guid
        rental = (RentalBO)super.read(id, conn);
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT * FROM Rental WHERE RevenueSourceID = '" + id + "'");
        conn.commit();
        //build the object from the result set
        if (rs.next()) {
            String productID = rs.getString("ProductID");
            rental.setDateOut(rs.getDate("DateOut").toString());
            rental.setDateDue(rs.getDate("DateDue").toString());
            if(rs.getDate("DateIn") != null){
                rental.setDateIn(rs.getDate("DateIn").toString());
            }else{
                String is = null;
                rental.setDateIn(is);
            }
            rental.setPricePerDay(rs.getDouble("PricePerDay"));
            ResultSet rs2 = stmt.executeQuery("SELECT * FROM PRODUCT WHERE ProductID = '" + productID + "'");
            if(rs2.next()){
                ForRentDAO forRentDAO = ForRentDAO.getInstance();
                rental.setProduct((ProductBO)forRentDAO.read(productID));
            }
            rental.setAlreadyInDB(true);
            rental.setIsDirty(false);
            // save to the cache
            c.put(rental.getRevSrcID(), rental);
        }else{
            throw new DataException("The RENTAL object was NOT found in the database.");
        }
        stmt.close();
        ConnectionPool.getInstance().release(conn);
        // return the BO
        return rental;
    }
    /**
     * This is the public readAll method.  It is what is called when
     * the user (controller) code wants to readAll a rental object
     * into the database.
     */
    public synchronized ArrayList<RevenueSourceBO> readAll() throws DataException, SQLException, ConnectionPoolException{
        ArrayList<RevenueSourceBO> list = new ArrayList<RevenueSourceBO>();
        //A query built to get read all data from the Rental Table
        String query = "SELECT * FROM Rental";
        
        Connection conn = ConnectionPool.getInstance().get();
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery(query);
        conn.commit();
        Cache c = Cache.getInstance();
        
        //Extracts the elements from the result set and builds a list of results
        //Then an object is built from result set
        while (rs.next()) {
            
            RentalBO rental = new RentalBO(rs.getString("RevenueSourceID"));
            c.put(rental.getRevSrcID(), rental);
            rental = (RentalBO)super.read(rs.getString("RevenueSourceID"), conn);
            String productID = rs.getString("ProductID");
            rental.setDateOut(rs.getDate("DateOut").toString());
            rental.setDateDue(rs.getDate("DateDue").toString());
            if(rs.getDate("DateIn") != null){
                rental.setDateIn(rs.getDate("DateIn").toString());
            }else{
                String is = null;
                rental.setDateIn(is);
            }
            rental.setPricePerDay(rs.getDouble("PricePerDay"));
            ResultSet rs2 = stmt.executeQuery("SELECT * FROM PRODUCT WHERE ProductID = '" + productID + "'");
            if(rs2.next()){
                ForRentDAO forRentDAO = ForRentDAO.getInstance();
                rental.setProduct((ProductBO)forRentDAO.read(productID));
            }
            rental.setAlreadyInDB(true);
            rental.setIsDirty(false);
            //Adds the RENTAL Revenue Source Business Object to the ArrayLIst
            list.add(rental);
        }//while
        // Closes the result set
        stmt.close();
        //Releases the connection back to the pool
        ConnectionPool.getInstance().release(conn);
        //Returns an ArrayList of RENTAL Revenue Source Business Objects
        return list;
    }
    
//******************************************************************************
//   UPDATE - Incorporates 1 Insert, & 1 Update methods
//******************************************************************************
    /**
     * This is the public save method.  It is what is called when
     * the user (controller) code wants to save or update an object
     * into the database.
     */
    synchronized void insert(RevenueSourceBO bo, Connection conn) throws SQLException, DataException {
        //update the DB
        RentalBO BO = (RentalBO)bo;
        PreparedStatement pstmt = conn.prepareStatement("INSERT INTO Rental (RevenueSourceID, ProductID, DateOut, DateIn, DateDue, PricePerDay)" +
                "VALUES (?,?,?,?,?,?)");
        pstmt.setString(1, BO.getRevSrcID());
        pstmt.setString(2, BO.getProduct().getProductID());
        pstmt.setDate(3, java.sql.Date.valueOf(BO.getDateOut()));
        if(BO.getDateIn() != null){
            pstmt.setDate(4, java.sql.Date.valueOf(BO.getDateIn()));
        }else{
            pstmt.setNull(4, java.sql.Types.DATE);
        }
        pstmt.setDate(5, java.sql.Date.valueOf(BO.getDateDue()));
        pstmt.setDouble(6, BO.getPricePerDay());
        pstmt.executeUpdate();
        pstmt.close();
        // tell the object that it's now in the DB (so we call update next time not insert)
        BO.setAlreadyInDB(true);
    }//end insert
    /**
     * This is the public save method.  It is what is called when
     * the user (controller) code wants to save or update an object
     * into the database.
     */
    protected void update(RevenueSourceBO bo, Connection conn) throws SQLException, DataException {
        //update the DB
        RentalBO BO = (RentalBO)bo;
        PreparedStatement pstmt = conn.prepareStatement("UPDATE Rental SET ProductID=?, DateOut=?, DateIn=?, DateDue=?, PricePerDay=? WHERE revenueSourceID = ?");
        pstmt.setString(1, BO.getProduct().getProductID());
        pstmt.setDate(2, java.sql.Date.valueOf(BO.getDateOut()));
        if(BO.getDateIn() != null){
            pstmt.setDate(3, java.sql.Date.valueOf(BO.getDateIn()));
        }else{
            pstmt.setNull(3, java.sql.Types.DATE);
        }
        pstmt.setDate(4, java.sql.Date.valueOf(BO.getDateDue()));
        pstmt.setDouble(5, BO.getPricePerDay());
        pstmt.setString(6, BO.getRevSrcID());
        pstmt.execute();
        pstmt.close();
        BO.setAlreadyInDB(true);
        
    }// end update
    
//******************************************************************************
//   DELETE - Does not incorporate any methods
//******************************************************************************
    /**
     * Any and all delete methods were intentionally left out of this
     * program becuase we specifically DO NOT want to give the users the
     * ability to delete ANYTHING.  All records will remain on file.
     * Archival options would recommended over deleting.
     **/
}
